/*
 * path.cpp
 *
 *  Created on: 2008-10-8
 *      Author: fanliangliang
 */

#include "path.h"
#include "string_util.h"
#include <sys/stat.h>
#include <unistd.h>
#include <climits>
#include <cstring>
#include <cctype>
#include <vector>

BEGIN_NAMESPACE_TOXIC

using namespace std;

#if defined(PLATFORM_WIN32)
const char* Path::SEPARATOR = "\\";
const char Path::SEPARATOR_CHAR = '\\';
const char* Path::PATH_SEPARATOR = ";";
const char Path::PATH_SEPARATOR_CHAR = ';';
#else
const char* Path::SEPARATOR = "/";
const char Path::SEPARATOR_CHAR = '/';
const char* Path::PATH_SEPARATOR = ":";
const char Path::PATH_SEPARATOR_CHAR = ':';
#endif

static int make_dir(const char* pathname) {
#if defined(PLATFORM_WIN32)
	return mkdir(pathname);
#else
	return mkdir(pathname, 0755);
#endif
}

static size_t get_start_index(char* pathname, size_t length) {
	for (size_t i = 0; i < length; ++i) {
		if (pathname[i] == '/') {
			pathname[i] = '\0';
			if (strcmp(pathname, "") == 0 ||
				strcmp(pathname, ".") == 0 ||
				strcmp(pathname, "..") == 0 ||
				(i == 2 && pathname[1] == ':')) {
				pathname[i] = '/';
				return i + 1;
			}
			else {
				pathname[i] = '/';
				return i;
			}
		}
	}
	return size_t(-1);
}

static bool is_file_exists(char* pathname, size_t i) {
	pathname[i] = '\0';
	int result = access(pathname, 0);
	pathname[i] = '/';
	return result == 0;
}

static size_t find_first_slash(char* pathname, const vector<size_t>& slashIndexs) {
	size_t indexCount = slashIndexs.size();
	if (indexCount == 0) {
		return size_t(-1);
	}

	if (indexCount == 1) {
		return is_file_exists(pathname, slashIndexs[0]) ? size_t(-1) : 0;
	}

	size_t low = 0;
	size_t high = indexCount - 1;
	size_t lowExists = size_t(-1);
	size_t highExists = size_t(-1);
	while (low != high - 1) {
		size_t middle = (low + high) / 2;
		bool exists = is_file_exists(pathname, slashIndexs[middle]);
		if (exists) {
			low = middle;
			lowExists = exists;
		}
		else {
			high = middle;
			highExists = exists;
		}
	}

	if (lowExists == size_t(-1)) {
		lowExists = is_file_exists(pathname, slashIndexs[low]);
	}

	if (highExists == size_t(-1)) {
		highExists = is_file_exists(pathname, slashIndexs[high]);
	}

	if (lowExists == 0) {
		return low;
	}
	return highExists == 0 ? high : size_t(-1);
}

bool createDirectory(const char* pathname) {
	char _pathname[PATH_MAX + 1] = { 0 };
	strncpy(_pathname, pathname, PATH_MAX - 1);
	size_t length = strlen(_pathname);
	if (_pathname[length - 1] != '/') {
		_pathname[length] = '/';
		++length;
	}

	vector<size_t> slashIndexs;
	for (size_t i = get_start_index(_pathname, length); i < length; ++i) {
		if (_pathname[i] == '/') {
			slashIndexs.push_back(i);
		}
	}

	size_t pos = find_first_slash(_pathname, slashIndexs);
	if (pos == size_t(-1)) {
		return true;
	}

	size_t indexCount = slashIndexs.size();
	for (size_t i = pos; i < indexCount; ++i) {
		size_t j = slashIndexs[i];
		_pathname[j] = '\0';
		if (make_dir(_pathname) != 0) {
			return false;
		}
		_pathname[j] = '/';
	}

	return true;
}

bool isAbsolutePath(const string& pathname) {
#if defined(PLATFORM_WIN32)
	return pathname.size() < 3 ? false : (isalpha(pathname[0]) && (pathname[1] == ':') && (pathname[2] == '\\'));
#else
	return pathname.empty() ? false : (pathname[0] == '/');
#endif
}

bool isRootPath(const string& pathname) {
#if defined(PLATFORM_WIN32)
	return pathname.size() != 3 ? false : (isalpha(pathname[0]) && (pathname[1] == ':') && (pathname[2] == '\\'));
#else
	return pathname.size() != 1 ? false : (pathname[0] == '/');
#endif
}

bool exists(const string& pathname) {
	return access(pathname.c_str(), 0) == 0;
}

string basename(const string& pathname) {
	if (isRootPath(pathname)) {
		return pathname;
	}

	string _pathname = rtrim(pathname, "/\\");
	string::size_type pos = _pathname.find_last_of("/\\");
	if (pos != string::npos) {
		return _pathname.substr(pos + 1);
	}
	return _pathname;
}

string basename(const string& pathname, const string& suffix) {
	string _basename = basename(pathname);
	if (endsWith(_basename, suffix)) {
		return _basename.substr(0, _basename.size() - suffix.size());
	}
	return _basename;
}

string dirname(const string& pathname) {
	if (isRootPath(pathname)) {
		return pathname;
	}

	string _pathname = rtrim(pathname, "/\\");
	string::size_type pos = _pathname.find_last_of("/\\");
	if (pos != string::npos) {
		return _pathname.substr(0, pos);
	}
	return ".";
}

string extname(const string& pathname) {
	string _basename = basename(pathname);
	string::size_type pos = _basename.rfind('.');
	if (pos != string::npos) {
		return _basename.substr(pos + 1);
	}
	return "";
}

END_NAMESPACE_TOXIC
